#include "WidgetAttributeType.h"

#include "GenericInfo.h"
#include "PlaceholderType.h"
#include "FunctionList.h"
#include "NativeFunction.h"
#include "FunctionType.h"

#include "Widgets/ReflectableWidgetAttribute.h"

#include "Object/Object.h"

#include "Reflection/TypeUtils.h"

CString CWidgetAttributeType::PlaceholderName = U"T";

CWidgetAttributeType::CWidgetAttributeType()
    :CType(
        STypeID(CType::GetEngineCoreNamespace()+U"::TWidgetAttribute",true)
        ,ETypeFlags::Generic | ETypeFlags::WidgetAttribute
        )
{
    GenericInfo = new CGenericInfo();
    auto PlaceholderType = new CPlaceholderType(
        STypeID(GetFullName()+U"::"+ PlaceholderName,true)
        , ETypeFlags::None
        , 0
        ,this
        );
    GenericInfo->PlaceholderTypes.Add(PlaceholderType);
 
    SDecoratedType DelegateType;
    DelegateType.BasicType = CScriptDelegateType::GetStaticType();
    DelegateType.OverrideGenericTypes.Add(PlaceholderType);

    //add functions
    Functions = new CFunctionList(this);


    //function to get value from widget attribute 
    {
        auto NativeFunction = new CNativeFunction(
            Functions
            , U"Get"
            , PlaceholderType
            , {}
            , {}
            );
    }

    //set value 
    {
        auto NativeFunction = new CNativeFunction(
        Functions
        , U"Set"
        ,  CGetType<void>()()
        , {PlaceholderType}
        , {}
        );
    }

    //set function
    {
        auto NativeFunction = new CNativeFunction(
        Functions
        , U"Set"
        ,  CGetType<void>()()
        , {
             DelegateType
            }
        , {}
        );
    }
}


CWidgetAttributeType * CreateWidgetAttributeType()
{
    CWidgetAttributeType *Type = new CWidgetAttributeType();
    return Type;
}

CWidgetAttributeType * CWidgetAttributeType::GetStaticType()
{
    static CWidgetAttributeType *StaticType = CreateWidgetAttributeType();
    return StaticType;
}

CWidgetAttributeTypeGeneralized *CWidgetAttributeType::FindAttributeType(CType *ValueType)
{
    auto Found = reinterpret_cast<CGenericInfo*>(CWidgetAttributeType::GetStaticType()
        ->GenericInfo)
        ->GenericMap.Find({ValueType});

    if (Found)
    {
        return (CWidgetAttributeTypeGeneralized *)*Found;
    }

    return nullptr;
}

CString CWidgetAttributeType::CreateTypeName(const CString &ElementType)
{
    return STypeID::CreateTemplateTypeName(
        GetStaticType()->GetFullName()
        ,{ElementType}
        ,{PlaceholderName}
        );
}

STypeID CWidgetAttributeType::CreateID(const SDecoratedType &ElementType)
{
    return STypeID(
        GetStaticType()->ID
        ,{ElementType.BasicType->ID}
        ,{PlaceholderName}
        );
}

static uint8_t RegisterWidgetAttributeType=(CWidgetAttributeType::GetStaticType(),0); 

class CWidgetAttributeInstance : public ITypeInstance
{
protected:
    CWidgetAttributeTypeGeneralized *Type;
public:
    CWidgetAttributeInstance(CWidgetAttributeTypeGeneralized *InType)
        :Type(InType)
    {
    }

    void InitInstance(void *Memory) override
    {
        new(Memory) CReflectbleWidgetAttribute();
    }

    void EraseInstance(void *Instance) override
    {
        reinterpret_cast<CReflectbleWidgetAttribute*>(Instance) -> ~CReflectbleWidgetAttribute();
    }

    void CloneInstance(const void *Instance, void *TargetMemory) override
    {
    new (TargetMemory) CReflectbleWidgetAttribute(*(CReflectbleWidgetAttribute*)Instance); 
    }

    uint64_t GetInstanceHash(const void *Instance) const override
    {
        return reinterpret_cast<const CReflectbleWidgetAttribute*>(Instance)-> GetHashCode();
    }

    bool IsInstanceEqual(const void *A, const void *B) const override
    {
        return  
            reinterpret_cast<const CReflectbleWidgetAttribute*>(A)->operator==(
                * reinterpret_cast<const CReflectbleWidgetAttribute*>(B)
            );
    }

    uint32_t GetSize() override
    {
        return sizeof(CReflectbleWidgetAttribute);
    }

    void *CreateInstance() override
    {
        return new CReflectbleWidgetAttribute();
    }

    void ResetAllocated(void *Memory) override
    {
        reinterpret_cast<CReflectbleWidgetAttribute*>(Memory)->Clear();
    }

    CString ToDebugString(const void *InInstance) const override
    {
        return Type->GetFullName();
    }

    bool IsForceConvertableTo(CType *Other) override
    {
        if(!Other->IsWidgetAttributeType())
        {
            return false;
        }

        return Type->GetElementType().BasicType->IsForceConvertableTo(
            reinterpret_cast< CWidgetAttributeTypeGeneralized*>(Other)->GetElementType().BasicType
        );
    }

};


CWidgetAttributeTypeGeneralized::CWidgetAttributeTypeGeneralized(
     const SDecoratedType& InElementType
    , CFunctionTypeGeneralized * InGetterFunctionType
    , CScriptDelegateGenerialized * InGetterDelegateType
    )
    :CType(
        CWidgetAttributeType::CreateID(InElementType)
        ,InElementType.BasicType->IsScriptType()? ETypeFlags::Generalized | ETypeFlags::WidgetAttribute | ETypeFlags::Script
                                      : ETypeFlags::Generalized | ETypeFlags::WidgetAttribute
    )
{
    GetterDelegateType=InGetterDelegateType;

    Instancable=new CWidgetAttributeInstance(this);
    GeneralizedInfo=new CGeneralizedInfo();
    GeneralizedInfo->TemplateType=CWidgetAttributeType::GetStaticType();
    GeneralizedInfo->TypeArguments.Add({InElementType.BasicType});

    GeneralizedInfo->TemplateType->GenericInfo->GeneralizedTypes.Add(this);


    reinterpret_cast<CGenericInfo *>(GeneralizedInfo->TemplateType->GenericInfo)
        ->GenericMap.Add(TVector<CType*>({InElementType.BasicType}), this);

    //add functions
    Functions=new CFunctionList(this);

    //function to get value from widget attribute 
    {
        auto NativeFunction=new CNativeFunction(
            Functions
            ,U"Get"
            , InElementType
            ,{}
            ,{}
            );

        NativeFunction->SetCppBody([InElementType](const TVector<CAny>& InArgs)->CAny
        {
            void*  ValuePtr= reinterpret_cast<CReflectbleWidgetAttribute*>(InArgs[0].GetPointer())
                ->GetValue(InElementType.BasicType);
            return CAny(InElementType.BasicType,ValuePtr);
        });
    }

    //set value
    {
        auto NativeFunction=new CNativeFunction(
            Functions
            ,U"Set"
            ,CGetType<void>()()
            ,{InElementType}
            ,{}
            );

        NativeFunction->SetCppBody([InElementType](const TVector<CAny>& InArgs)->CAny
        {
            CReflectbleWidgetAttribute* This = *reinterpret_cast<CReflectbleWidgetAttribute**>(InArgs[0].GetPointer());
            assert(InArgs[1].GetType()->IsForceConvertableTo(InElementType.BasicType));
            This->SetValue(InElementType.BasicType,InArgs[1].GetPointer());
            return CAny();
        });

    }

    //set function
    {
        auto NativeFunction=new CNativeFunction(
            Functions
            ,U"Set"
            ,CGetType<void>()()
            ,{ InGetterDelegateType }
            ,{}
            );

        NativeFunction->SetCppBody([InElementType,InGetterDelegateType](const TVector<CAny>& InArgs)->CAny
        {
            CReflectbleWidgetAttribute* This = *reinterpret_cast<CReflectbleWidgetAttribute**>(InArgs[0].GetPointer());
            
            assert(InArgs[1].GetType()->IsForceConvertableTo(InGetterDelegateType));
            SScriptDelegateBase * Delegate= reinterpret_cast<SScriptDelegateBase*>(InArgs[1].GetPointer());
            This->SetDelegate(*Delegate);
            return CAny();
        });
    
    }
}


SDecoratedType CWidgetAttributeTypeGeneralized::GetElementType() const
{
    return GeneralizedInfo->TypeArguments[0];
}